﻿using AutoMapper;
using EnetaMvc.ApplicationServices.Dto;
using EnetaMvc.Core.Domain.Common;
using EnetaMvc.Core.Domain.Forums;
using EnetaMvc.Core.Repositories;
using EnetaMvc.Infrastructure;
using System;
using System.Collections.Generic;
using System.Linq;

namespace EnetaMvc.ApplicationServices
{
    public class ForumService : IForumService
    {
        private readonly IForumRepository _forumRepository;
        private readonly IUserRepository _userRepository;
        private readonly IPublishStateRepository _publishStateRepository;

        public ForumService(IForumRepository forumRepository, IUserRepository userRepository, IPublishStateRepository publishStateRepository)
        {
            _forumRepository = forumRepository;
            _userRepository = userRepository;
            _publishStateRepository = publishStateRepository;

            Mapper.CreateMap<User, UserDto>();
            Mapper.CreateMap<ForumPost, ForumPostDto>()
                  .ForMember(m => m.NumberOfReplies, m => m.MapFrom(x => x.Replies.Count))
                  .ForMember(m => m.LastPost, m => m.MapFrom(x => x.GetLastPost()))
                  .ForMember(m => m.TopLevelPostId, m => m.MapFrom(x => x.GetTopLevelPost().Id));
            Mapper.CreateMap<Forum, ForumDto>()
                  .ForMember(m => m.TopicsCount, m => m.MapFrom(x => _forumRepository.GetTopicCountInForum(x.Id)))
                  .ForMember(m => m.PostsCount, m => m.MapFrom(x => _forumRepository.GetPostCountInForum(x.Id)))
                  .ForMember(m => m.LastPost, m => m.MapFrom(x => _forumRepository.GetLastPostForForum(x.Id)));
            Mapper.CreateMap<ForumDto, Forum>()
                  .ForMember(m => m.Category, m => m.Ignore())
                  .ForMember(m => m.Id, m => m.Ignore())
                  .ForMember(m => m.Moderators, m => m.Ignore())
                  .ForMember(m => m.Posts, m => m.Ignore());
            Mapper.CreateMap<ForumCategory, ForumCategoryDto>();
            Mapper.CreateMap<ForumCategoryDto, ForumCategory>()
                  .ForMember(m => m.Forums, m => m.Ignore())
                  .ForMember(m => m.Id, m => m.Ignore());
            Mapper.CreateMap<PagedResult<ForumPost>, PagedResult<ForumPostDto>>()
                  .ForMember(m => m.Results, m => m.Ignore());
        }

        public IList<ForumCategoryDto> ListForumCategories()
        {
            var categories = _forumRepository.ListForumCategories();
            var categoryDtos = new List<ForumCategoryDto>();

            Mapper.Map(categories, categoryDtos);

            return categoryDtos;
        }


        public ForumCategoryDto GetForumCategoryById(long id)
        {
            var category = _forumRepository.GetForumCategoryById(id);
            var categoryDto = new ForumCategoryDto();

            Mapper.Map(category, categoryDto);

            return categoryDto;
        }


        public ForumDto GetForumById(long id)
        {
            var forum = _forumRepository.GetForumById(id);
            var forumDto = new ForumDto();

            Mapper.CreateMap<Forum, ForumDto>();
            Mapper.Map(forum, forumDto);

            return forumDto;
        }


        public PagedResult<ForumPostDto> ListPostsForForum(long id, int page, int pageSize)
        {
            var postResult = _forumRepository.ListPostsForForum(id, page, pageSize);
            var postDtoResult = new PagedResult<ForumPostDto>();
            postDtoResult.Results = new List<ForumPostDto>();

            Mapper.Map(postResult, postDtoResult);
            Mapper.Map(postResult.Results, postDtoResult.Results);

            return postDtoResult;
        }


        public IList<ForumPostDto> GetForumThread(long postId)
        {
            var post = _forumRepository.GetForumPostById(postId);
            if (post.Parent != null)
                post = _forumRepository.GetForumPostById(post.Id);

            var posts = new List<ForumPost>();
            posts.Add(post);
            posts.AddRange(post.Replies);

            var postDtos = new List<ForumPostDto>();

            Mapper.Map(posts, postDtos);

            return postDtos;
        }

        public ForumDto GetForumByPostId(long id)
        {
            var forum = _forumRepository.GetForumByPostId(id);
            var forumDto = new ForumDto();

            Mapper.Map(forum, forumDto);

            return forumDto;
        }

        public ForumPostDto GetPostById(long id)
        {
            var post = _forumRepository.GetForumPostById(id);
            var postDto = new ForumPostDto();

            Mapper.Map(post, postDto);

            return postDto;
        }


        public void AddReplyToPost(long postId, string author, string replyText)
        {
            var post = _forumRepository.GetForumPostById(postId);
            var user = _userRepository.GetUserByLiveIdToken(author);

            var reply = new ForumPost();
            reply.Body = replyText;
            reply.Created = DateTime.Now;
            reply.CreatedBy = user;
            reply.Forum = post.Forum;
            reply.Modified = DateTime.Now;
            reply.ModifiedBy = user;
            reply.Parent = post.GetTopLevelPost();
            reply.PublishState = _publishStateRepository.GetPublishStateById(2);
            reply.PublishStateBy = user;
            reply.PublishStateDate = DateTime.Now;
            reply.Title = post.Title;

            _forumRepository.SaveForumPost(reply);
        }


        public void AddTopicToForum(long forumId, string author, string title, string body)
        {
            var forum = _forumRepository.GetForumById(forumId);
            var user = _userRepository.GetUserByLiveIdToken(author);

            var topic = new ForumPost();
            topic.Body = body;
            topic.Created = DateTime.Now;
            topic.CreatedBy = user;
            topic.Forum = forum;
            topic.Modified = DateTime.Now;
            topic.ModifiedBy = user;
            topic.Parent = null;
            topic.PublishState = _publishStateRepository.GetPublishStateById(2);
            topic.PublishStateBy = user;
            topic.PublishStateDate = DateTime.Now;
            topic.Title = title;

            _forumRepository.SaveForumPost(topic);
        }



        public void IncreaseTopicViewCount(long topicId)
        {
            var topic = _forumRepository.GetForumPostById(topicId);
            if (topic == null)
                return;

            var topLevelPost = topic.GetTopLevelPost();
            if (topLevelPost == null)
                return;

            topLevelPost.Views++;
            _forumRepository.SaveForumPost(topLevelPost);
        }

        public IList<ForumPostDto> ListLastTopicsForRss()
        {
            var topics = _forumRepository.ListLastTopicsForRss();
            var topicDtos = new List<ForumPostDto>();

            Mapper.Map(topics, topicDtos);

            return topicDtos;
        }

        public IList<ForumPostDto> ListLastRepliesForRss()
        {
            var topics = _forumRepository.ListLastRepliesForRss();
            var topicDtos = new List<ForumPostDto>();

            Mapper.Map(topics, topicDtos);

            return topicDtos;
        }

        public IList<ForumPostDto> ListLastTopicsForCategoryRss(long categoryId)
        {
            var topics = _forumRepository.ListLastTopicsForCategoryRss(categoryId);
            var topicDtos = new List<ForumPostDto>();

            Mapper.Map(topics, topicDtos);

            return topicDtos;
        }

        public IList<Tuple<ForumDto, IList<ForumPostDto>>> ListLastTopicsOverForums()
        {
            IList<long> excludeForums = new List<long>();
            var result = new List<Tuple<ForumDto, IList<ForumPostDto>>>();

            for (var i = 0; i < 3; i++)
            {
                var topics = _forumRepository.ListPostsOverForumsExclusive(excludeForums.ToArray(), 3);
                if (topics.Count == 0)
                    break;

                excludeForums.Add(topics[0].Forum.Id);

                topics = _forumRepository.ListPostsForForum(topics[0].Forum.Id, 1, 4).Results;

                var forumDto = new ForumDto();
                var postDtos = new List<ForumPostDto>();

                Mapper.Map(topics[0].Forum, forumDto);
                Mapper.Map(topics, postDtos);

                var tuple = new Tuple<ForumDto, IList<ForumPostDto>>(forumDto, postDtos);
                result.Add(tuple);
            }

            return result;
        }

        public void SaveCategory(ForumCategoryDto categoryDto)
        {
            if (categoryDto == null)
                return;

            var category = new ForumCategory();
            if (categoryDto.Id > 0)
            {
                category = _forumRepository.GetForumCategoryById(categoryDto.Id);
                if (category == null)
                { 
                    // it's deleted
                    return;
                }
            }

            Mapper.Map(categoryDto, category);
            _forumRepository.SaveForumCategory(category);
        }

        public void DeleteCategory(long id)
        {
            var category = _forumRepository.GetForumCategoryById(id);
            if (category == null)
                return;

            _forumRepository.DeleteForumCategory(category);
        }

        public void SaveForum(ForumDto forumDto)
        {
            if (forumDto == null)
                return;

            var forum = new Forum();
            if (forumDto.Id > 0)
            {
                forum = _forumRepository.GetForumById(forumDto.Id);
            }

            forum.Category = _forumRepository.GetForumCategoryById(forumDto.CategoryId);
            if (forumDto.Moderators != null)
            {
                for (var i = forum.Moderators.Count - 1; i >= 0; i--)
                {
                    var moder = forum.Moderators[i];
                    var moderDto = forumDto.Moderators.FirstOrDefault(u => u.UserName == moder.UserName);
                    if (moderDto != null)
                        continue;

                    forum.Moderators.RemoveAt(i);
                    moder.ModeratorOf.Remove(forum);
                    _userRepository.SaveUser(moder);
                }

                foreach (var moder in forumDto.Moderators)
                {
                    var moderUser = forum.Moderators.FirstOrDefault(m => m.UserName == moder.UserName);
                    if (moderUser != null)
                        continue;

                    moderUser = _userRepository.GetUserByUserName(moder.UserName);
                    if (moderUser == null)
                        continue;

                    forum.Moderators.Add(moderUser);
                    moderUser.ModeratorOf.Add(forum);
                    _userRepository.SaveUser(moderUser);
                }
            }
            Mapper.Map(forumDto, forum);

            _forumRepository.SaveForum(forum);
        }

        public void DeleteForum(long id)
        {
            var forum = _forumRepository.GetForumById(id);
            if (forum == null)
                return;

            _forumRepository.DeleteForum(forum);
        }

        public void DeletePost(long id)
        {
            var post = _forumRepository.GetForumPostById(id);
            if (post == null)
                return;

            _forumRepository.DeleteForumPost(post);
        }
    }
}
